#include "ptwrap.hpp"
#include <deque>
#include <stdio.h>
#include <assert.h>

// [[ex04ba]]
// DEBUG descr The queue with automatic message batching.
template <class Value>
class Queue
{
public:
	Queue(int limit = 1000) :
		limit_(limit), 
		dqwr_(&deque1_), dqrd_(&deque2_), condrd_(condwr_)
	{ }

	virtual ~Queue()
	{ }

	// this combines the STL front() and pop_front()
	// since the popping must be atomic
	Value pop_front()
	{
		pw::lockmutex lm(mutexrd_);

		while(dqrd_->empty()) {
			pw::lockmutex lm(condwr_);

			if (dqwr_->empty()) {
				// it releases and gets back the mutex
				condrd_.wait(); 
			} else {
				swap(dqwr_, dqrd_);
				// in case if the write buffer was full,
				// wake up the writers
				condwr_.broadcast();
			}
		}

		Value v = dqrd_->front();
		dqrd_->pop_front();
		return v;
	}

	void push_back(Value &v)
	{
		pw::lockmutex lm(condwr_);

		while (dqwr_->size() >= limit_)
			// it releases and gets back the mutex
			condwr_.wait(); 

		dqwr_->push_back(v);
		condrd_.broadcast();
	}

protected:
	int limit_;
	std::deque<Value> deque1_, deque2_;
	pw::pmutex mutexrd_; // protects *dqrd_
	std::deque<Value> *dqwr_, *dqrd_;
	pw::pmcond condwr_;
	pw::pchaincond condrd_;
};
// END


// [[ex04bb]]
// DEBUG descr Message with the type indicator.
struct Message 
{
	virtual ~Message() { } // allow for polymorphism

	enum {
		EXIT,
		NORMAL // first normal type
	};
	int msgtype_;
	// ... whatever else is needed by the application ...
};
// END

// [[ex04bc]]
// DEBUG descr A queue with message ownership destroys the unhandled messages on exit.
template <class Msg>
class PointerQueue : public Queue<Msg *>
{
public:
	PointerQueue(int limit = 1000) : Queue<Msg *>(limit)
	{ }
	virtual ~PointerQueue()
	{
		Msg *m;
		while (!Queue<Msg *>::deque1_.empty()) {
			m = Queue<Msg *>::deque1_.front();
			Queue<Msg *>::deque1_.pop_front();
			delete m;
		}
		while (!Queue<Msg *>::deque2_.empty()) {
			m = Queue<Msg *>::deque2_.front();
			Queue<Msg *>::deque2_.pop_front();
			delete m;
		}
	}
	// the reference argument of the base class is quite
	// inconvenient here, a wrapper makes it more usable
	inline void push_back(Msg *v)
	{
		Queue<Msg *>::push_back(v);
	}
};
// END

// DEBUG {
struct data : public Message
{
	int v_;

	data(int v) : v_(v)
	{ }
	~data()
	{
		fprintf(stderr, "deleted data %d\n", v_);
	}
};

int main()
{
	PointerQueue<data> q;
	q.push_back(new data(1));
	q.push_back(new data(2));
	data * v2 = q.pop_front();
	assert(v2->v_ == 1);
	delete v2;
	return 0;
}
/* Sample output:
deleted data 1
deleted data 2
*/
// DEBUG }
